// * JSON.stringify 将对象转换为 JSON 字符串（也支持嵌套对象转换）
// * JSON.parse 将 JSON 字符串解码（复原）

let student = {
	name: 'John',
	age: 30,
	isAdmin: false,
	courses: ['html', 'css', 'js'],
	wife: null,
}

let json = JSON.stringify(student)

// alert(json) // string {"name":"John","age":30,"isAdmin":false,"courses":["html","css","js"],"wife":null}

// *************************************************************************************************************

// * JSON编码的对象有个重要的特点：
// * 1. 字符串使用双引号。JSON 中没有单引号或反引号。所以 'John' 会被转换为 "John"。
// * 2. 对象属性名称也是双引号。这是强制性的。所以 age: 30 被转为 "age": 30。
// * 3. JSON 支持这些数据类型：Objects {...}, Arrays [...], Primitives (strings, numbers, boolean, null)。

console.log(JSON.stringify([1, 2, { a: 3 }])) // "[1,2,{"a":3}]"
console.log(JSON.stringify(1)) // "1"
console.log(JSON.stringify('1')) // "1"
console.log(JSON.stringify(true)) // "true"
console.log(JSON.stringify(null)) // "null"

// * JSON.stringify 会忽略：函数，undefined，Symbol。

let user = {
	sayHi() {
		alert('Hello')
	},
	[Symbol('id')]: 1,
	something: undefined,
}

console.log(JSON.stringify(user)) // "{}"

// *************************************************************************************************************

// * 排除和转换：replacer
// * JSON.stringify 的完整语法是：JSON.stringify(value[, replacer[, space]]) value 是要编码的值，replacer 要编码的属性数组或函数，space 用于格式化的空格数量。

let room = {
	number: 23,
}

let meetup = {
	title: 'Conference',
	participants: [{ name: 'John' }, { name: 'Alice' }],
	place: room, // rome => { number: 23, occupiedBy: {...meetup} }
}

room.occupiedBy = meetup

// 1. 报错（因为两个对象相互引用）。
// console.log(JSON.stringify(meetup))

// 2. 这里指定了只转换 title 和 participants 属性，所以不会报错（但由于 participants 中属性没有被指定所以也会被忽略）。
console.log(JSON.stringify(meetup, ['title', 'participants'])) // {"title":"Conference","participants":[{},{}]}

// 3. 这里指定了要转换的对象中更多的属性。
console.log(JSON.stringify(meetup, ['title', 'participants', 'name', 'place', 'number'])) // {"title":"Conference","participants":[{"name":"John"},{"name":"Alice"}],"place":{"number":23}}

// 4. 使用函数形式指定要转换的属性（好处是不用手动的指定）。
console.log(JSON.stringify(meetup, (key, value) => (key == 'occupiedBy' ? undefined : value))) // {"title":"Conference","participants":[{"name":"John"},{"name":"Alice"}],"place":{"number":23;

// *************************************************************************************************************

// * 格式化（日志记录/美化输出）

let user2 = {
	name: 'John',
	age: 25,
	roles: {
		isAdmin: false,
		isEditor: true,
	},
}

console.log(JSON.stringify(user2, null, 2))
// {
//   "name": "John",
//   "age": 25,
//   "roles": {
//     "isAdmin": false,
//     "isEditor": true
//   }
// }

// *************************************************************************************************************

// 自定义 “toJSON”。我们可以自定义 JSON.stringify() 的行为，通过定义一个 “toJSON” 方法，使用 JSON.stringify 会自动调用它。

let obj = {
	number: 23,
}

let meetups = {
	title: 'Conference',
	date: new Date(Date.UTC(2017, 0, 1)),
	obj,
}

// 1. 直接转换
console.log(JSON.stringify(meetups, null, 2))
// {
//   "title": "Conference",
//   "date": "2017-01-01T00:00:00.000Z", 日期对象都有一个内建的 toJSON 方法来返回这种类型的字符串。
//   "obj": {
//     "number": 23
//   }
// }

// 2. 自定义对象的 toJSON
obj.toJSON = function () {
	return this.number
}

console.log(JSON.stringify(meetups, null, 2))
// {
//   "title": "Conference",
//   "date": "2017-01-01T00:00:00.000Z",
//   "obj": 23
// }

// *************************************************************************************************************

// * JSON.parse 解码 JSON 字符串，语法：JSON.parse(text[, reviver])。也支持嵌套对象。

// 1. 解析字符串化的数组
let arr = '[1, 2, 3]'
console.log(JSON.parse(arr)) // [ 1, 2, 3 ]

// 2. 解析boolean值
let bool = 'true'
console.log(JSON.parse(bool)) // true

// * 使用 reviver

// 例子：
// 服务器返回的 JSON 字符串
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}'

// 转换（反序列）
let res = JSON.parse(str)
console.log(res) // { title: 'Conference', date: '2017-11-30T12:00:00.000Z' }

//  报错，因为 date 的值不会被解析为 Date 对象。
// res.date.getTime()

// 使用 reviver 进行处理
let res2 = JSON.parse(str, (key, value) => (key === 'date' ? new Date(value) : value)) // 如果key是date，那么date的值就是一个日期对象
console.log(res2.date.getTime()) // 1512043200000
